/*******************************************************************************
 * Copyright (c) 2011 LegSem.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl-3.0.txt
 * 
 * Contributors:
 *     LegSem - initial API and implementation
 ******************************************************************************/
package com.legstar.eci.xmi;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;

import com.legstar.eci.xmi.model.AggregateInstanceTD;
import com.legstar.eci.xmi.model.ArrayDescr;
import com.legstar.eci.xmi.model.BindingEciOperation;
import com.legstar.eci.xmi.model.BindingTypeMapping;
import com.legstar.eci.xmi.model.ExternalDecimalTD;
import com.legstar.eci.xmi.model.IntegerTD;
import com.legstar.eci.xmi.model.PackedDecimalTD;
import com.legstar.eci.xmi.model.PhysicalFormats;
import com.legstar.eci.xmi.model.PlatformCompilerInfo;
import com.legstar.eci.xmi.model.SimpleInstanceTD;
import com.legstar.eci.xmi.model.StringTD;
import com.legstar.eci.xmi.model.TypeDescriptorMap;
import com.legstar.eci.xmi.model.WsdlBinding;
import com.legstar.eci.xmi.model.WsdlDefinitions;
import com.legstar.eci.xmi.model.WsdlImport;
import com.legstar.eci.xmi.model.XMI;
import com.thoughtworks.xstream.io.xml.QNameMap;

/**
 * Rudimentary configuration. TODO externalize values into a properties file
 */
public class EciXmiConfig {

    /** Namespaces namespace. */
    public static final String NS_NS = "http://www.w3.org/2000/xmlns/";

    /** WS-Addressing namespace. */
    public static final String ADDRESSING_NS = "http://schemas.xmlsoap.org/ws/2004/08/addressing";

    /** XML Schema namespace. */
    public static final String XSD_NS = "http://www.w3.org/2001/XMLSchema";

    /** SOAP namespace. */
    public static final String SOAP_NS = "http://schemas.xmlsoap.org/wsdl/soap/";

    /** WSDL namespace. */
    public static final String WSDL_NS = "http://schemas.xmlsoap.org/wsdl/";

    /** WSDL prefix. */
    public static final String WSDL_PFX = "";

    /** ECI XMI physical representation namespace. */
    public static final String ECI_XMI_PHYSICAL_REP_NS = "http://schemas.xmlsoap.org/wsdl/physicalrep/";

    /** ECI XMI physical representation prefix. */
    public static final String ECI_XMI_PHYSICAL_REP_PFX = "phy";

    /** Physical formats class. */
    public static final Class < ? > PHYSICAL_FORMATS_CLASS = PhysicalFormats.class;

    /** XMI namespace. */
    public static final String XMI_NS = "http://www.omg.org/XMI";

    /** XMI prefix. */
    public static final String XMI_PFX = "xmi";

    /** XMI class. */
    public static final Class < ? > XMI_CLASS = XMI.class;

    /** ECI XMI physical representation descriptor map namespace. */
    public static final String ECI_XMI_TYPE_DESCRIPTOR_MAP_NS = "physicalrep.xmi";

    /** ECI XMI physical representation descriptor map prefix. */
    public static final String ECI_XMI_TYPE_DESCRIPTOR_MAP_PFX = "physicalrep";

    /** Type descriptor map class. */
    public static final Class < ? > TYPE_DESCRIPTOR_MAP_CLASS = TypeDescriptorMap.class;

    /** Array descriptor class. */
    public static final Class < ? > ARRAY_DESCR_CLASS = ArrayDescr.class;

    /** ECI XMI type descriptor namespace. */
    public static final String ECI_XMI_TYPE_DESCRIPTOR_NS = "TypeDescriptor.xmi";

    /** ECI XMI type descriptor prefix. */
    public static final String ECI_XMI_TYPE_DESCRIPTOR_PFX = "TypeDescriptor";

    /**
     * Type descriptor classes. This is needed to help XmlStream identify ID
     * types.
     */
    public static final List < Class < ? > > ECI_XMI_TYPE_DESCRIPTOR_CLASSES = Arrays
            .asList(new Class < ? >[] { AggregateInstanceTD.class,
                    PackedDecimalTD.class, PlatformCompilerInfo.class,
                    SimpleInstanceTD.class, StringTD.class, IntegerTD.class,
                    ExternalDecimalTD.class });

    /** ECI format binding namespace. */
    public static final String ECI_FORMAT_BINDING_NS = "http://schemas.xmlsoap.org/wsdl/formatbinding/";

    /** ECI format binding prefix. */
    public static final String ECI_FORMAT_BINDING_PFX = "format";

    /** Format binding class. */
    public static final Class < ? > FORMAT_BINDING_CLASS = BindingTypeMapping.class;

    /** ECI CICS binding namespace. */
    public static final String ECI_CICS_BINDING_NS = "http://schemas.xmlsoap.org/wsdl/cicseci/";

    /** ECI CICS binding prefix. */
    public static final String ECI_CICS_BINDING_PFX = "cicseci";

    /** ECI operation binding class. */
    public static final Class < ? > ECI_OPERATION_BINDING_CLASS = BindingEciOperation.class;

    /** WSDL binding class. */
    public static final Class < ? > WSDL_BINDING_CLASS = WsdlBinding.class;

    /** WSDL import class. */
    public static final Class < ? > WSDL_IMPORT_CLASS = WsdlImport.class;

    /** WSDL definitions class. */
    public static final Class < ? > WSDL_DEFINITIONS_CLASS = WsdlDefinitions.class;

    /**
     * @return a map qualified class names mapped to classes
     */
    public static QNameMap getQNameMap() {

        QNameMap qNameMap = new QNameMap();
        qNameMap.setDefaultNamespace(WSDL_NS);
        qNameMap.setDefaultPrefix(WSDL_PFX);

        qNameMap.registerMapping(
                new QName(ECI_XMI_PHYSICAL_REP_NS,
                        getUnqualName(PHYSICAL_FORMATS_CLASS),
                        ECI_XMI_PHYSICAL_REP_PFX), PHYSICAL_FORMATS_CLASS);

        qNameMap.registerMapping(new QName(XMI_NS, getUnqualName(XMI_CLASS),
                XMI_PFX), XMI_CLASS);

        qNameMap.registerMapping(new QName(ECI_XMI_TYPE_DESCRIPTOR_MAP_NS,
                getUnqualName(TYPE_DESCRIPTOR_MAP_CLASS),
                ECI_XMI_TYPE_DESCRIPTOR_MAP_PFX), TYPE_DESCRIPTOR_MAP_CLASS);

        for (Class < ? > clazz : ECI_XMI_TYPE_DESCRIPTOR_CLASSES) {
            qNameMap.registerMapping(new QName(ECI_XMI_TYPE_DESCRIPTOR_NS,
                    getUnqualName(clazz), ECI_XMI_TYPE_DESCRIPTOR_PFX), clazz);
        }

        qNameMap.registerMapping(new QName(ECI_FORMAT_BINDING_NS,
                getUnqualName(FORMAT_BINDING_CLASS), ECI_FORMAT_BINDING_PFX),
                FORMAT_BINDING_CLASS);

        qNameMap.registerMapping(new QName(ECI_CICS_BINDING_NS,
                getUnqualName(ECI_OPERATION_BINDING_CLASS),
                ECI_CICS_BINDING_PFX), ECI_OPERATION_BINDING_CLASS);

        qNameMap.registerMapping(new QName(WSDL_NS,
                getUnqualName(WSDL_BINDING_CLASS), WSDL_PFX),
                WSDL_BINDING_CLASS);

        qNameMap.registerMapping(new QName(WSDL_NS,
                getUnqualName(WSDL_IMPORT_CLASS), WSDL_PFX), WSDL_IMPORT_CLASS);

        qNameMap.registerMapping(new QName(WSDL_NS,
                getUnqualName(WSDL_DEFINITIONS_CLASS), WSDL_PFX),
                WSDL_DEFINITIONS_CLASS);

        return qNameMap;
    }

    /**
     * @return a mapping between attributes and namespaces. This is for
     *         attributes which gets prefixed in the XML
     */
    public static Map < String, String > getAttrNSMap() {
        Map < String, String > attrNSMap = Collections
                .synchronizedMap(new HashMap < String, String >());

        attrNSMap.put(getUnqualName(XMI_CLASS) + "#" + "version", XMI_NS);

        attrNSMap.put(getUnqualName(TYPE_DESCRIPTOR_MAP_CLASS) + "#" + "id",
                XMI_NS);

        for (Class < ? > clazz : ECI_XMI_TYPE_DESCRIPTOR_CLASSES) {
            attrNSMap.put(getUnqualName(clazz) + "#" + "id", XMI_NS);
        }

        attrNSMap.put("arrayDescr" + "#" + "id", XMI_NS);

        return attrNSMap;
    }

    /**
     * Return a class unqualified (no package prefix) name.
     * 
     * @param clazz the class
     * @return the unqualified name
     */
    public static String getUnqualName(final Class < ? > clazz) {
        String unqname = clazz.getName();
        if (unqname.lastIndexOf('.') > 0) {
            unqname = unqname.substring(unqname.lastIndexOf('.') + 1);
        }
        return unqname;
    }

}
